/*
 * FNISVGEditor - JavaScript - based editor for SVG graphics 
 * Copyright (C) 2008 - 2009 Antti Leppä / Foyt
 * http://www.foyt.fi
 * 
 * License: 
 * 
 * Licensed under GNU Lesser General Public License Version 2.1 or later (the "LGPL") 
 * http://www.gnu.org/licenses/lgpl.html
 * 
 * Most of the color conversion algorithms used here are from GIMP's (www.gimp.org) source code. 
 * 
 * To be more specific most of the RGB handling routines are from libgimpcolor/gimprgb.c and 
 * HSV <> RBG coversion from libgimpcolor/gimpcolorspace.c 
 */

FNISVGEditorStylePlugin = Class.create(FNISVGEditorPlugin,
/**
 * @scope FNISVGEditorStylePlugin.prototype
 */
{
  /**
   * Constructor 
   * 
   * @param {Object} $super superclass
   * @param {FNISVGEditor} editorInstance instance of loading svgeditor  
   */
  initialize : function($super, editorInstance, pluginURL) {
    $super(editorInstance, pluginURL);

    var fillColor = editorInstance.getStyleManager().getFillColor();
    var fillOpacity = editorInstance.getStyleManager().getFillOpacity();
    var strokeColor = editorInstance.getStyleManager().getStrokeColor();
    var strokeOpacity = editorInstance.getStyleManager().getStrokeOpacity();

    if (fillColor == 'none') {
      fillColor = '#ffffff';
      fillOpacity = 0;
    }

    if (strokeColor == 'none') {
      strokeColor = '#000000';
      strokeOpacity = 0;
    }

    this._fillColorButton = new FNISVGEditorStylePlugin_ColorButton(fillColor, fillOpacity, this);
    this._fillColorButton.domNode.addClassName('pickFillColorButton');
    this._fillColorButton.setTooltip(SVGLocale.getText("stylePlugin_fillColorButtonTooltip"));
    editorInstance.registerToolbarItem('changeForegroundColor', this._fillColorButton);

    this._strokeColorButton = new FNISVGEditorStylePlugin_ColorButton(strokeColor, strokeOpacity, this);
    this._strokeColorButton.domNode.addClassName('pickStrokeColorButton');
    this._strokeColorButton.setTooltip(SVGLocale.getText("stylePlugin_strokeColorButtonTooltip"));
    editorInstance.registerToolbarItem('changeStrokeColor', this._strokeColorButton);
    
    this._strokeStyleButton = new FNIPushButton( {});
    this._strokeStyleButton.domNode.addClassName('fniSVGEditor_StylePlugin_strokeStyle_Button');
    this._strokeStyleButton.setTooltip(SVGLocale.getText("stylePlugin_strokeStyleButtonTooltip"));
    this._strokeStyleButton.setImage(this.getPluginURL() + "/gfx/icons/22x22/tools/stroke-style.png");
    editorInstance.registerToolbarItem('changeStrokeStyle', this._strokeStyleButton);

    this._strokeColorButton.addListener('buttonClick', this, this._onStokeColorButtonClick);
    this._fillColorButton.addListener('buttonClick', this, this._onFillColorButtonClick);
    this._strokeStyleButton.addListener('buttonClick', this, this._onStrokeStyleButtonClick);

    this._colorpicker = null;
    this._colorPanel = null;
  },
  /**
   * Returns plugins name
   */
  getName : function() {
    return "style";
  },
  _setStrokeColor : function(color, opacity) {
    this._strokeColorButton.setColor(color);
    this._strokeColorButton.setOpacity(opacity);
    this.getEditorInstance().getStyleManager().setStrokeColor(color);
    this.getEditorInstance().getStyleManager().setStrokeOpacity(opacity);

    var selectedElements = this.getEditorInstance().getSelectionManager().getSelectedElements();
    if (selectedElements.length > 0) {
      var undoGroup = new FNISVGEditorUndoableEventGroupEvent();
      for ( var i = 0; i < selectedElements.length; i++) {
        var element = selectedElements[i];
        undoGroup.addEvent(new FNISVGEditorUndoableElementStyledEvent(element, element.getAttribute("style")));
        FNISVGEditorElementStylingUtils.setStyles(element, {
          'stroke' : color,
          'stroke-opacity' : opacity
        });
      }

      this.getEditorInstance().getUndoManager().addUndoableEvent(undoGroup);
    }
  },
  _setFillColor : function(color, opacity) {
    this._fillColorButton.setColor(color);
    this._fillColorButton.setOpacity(opacity);
    this.getEditorInstance().getStyleManager().setFillColor(color);
    this.getEditorInstance().getStyleManager().setFillOpacity(opacity);

    var selectedElements = this.getEditorInstance().getSelectionManager().getSelectedElements();

    if (selectedElements.length > 0) {
      var undoGroup = new FNISVGEditorUndoableEventGroupEvent();
      for ( var i = 0; i < selectedElements.length; i++) {
        var element = selectedElements[i];
        undoGroup.addEvent(new FNISVGEditorUndoableElementStyledEvent(element, element.getAttribute("style")));
        FNISVGEditorElementStylingUtils.setStyles(element, {
          'fill' : color,
          'fill-opacity' : opacity
        });
      }
      this.getEditorInstance().getUndoManager().addUndoableEvent(undoGroup);
    }
  },
  /**
   * @private
   */
  _onStokeColorButtonClick : function(event) {
    var styleManager = this.getEditorInstance().getStyleManager();
    var selectionManager = this.getEditorInstance().getSelectionManager();

    var strokeColor = styleManager.getStrokeColor();
    var strokeOpacity = styleManager.getStrokeOpacity();

    if (selectionManager.getSelectedElements().length == 1) {
      var selectedElement = selectionManager.getSelectedElements()[0];
      strokeColor = FNISVGEditorElementStylingUtils.getStyle(selectedElement, "stroke");
      strokeOpacity = FNISVGEditorElementStylingUtils.getStyle(selectedElement, "stroke-opacity");
    }

    this._openColorPicker("stroke", event.component, strokeColor, strokeOpacity);
  },
  _onFillColorButtonClick : function(event) {
    var styleManager = this.getEditorInstance().getStyleManager();
    var selectionManager = this.getEditorInstance().getSelectionManager();

    var fillColor = styleManager.getFillColor();
    var fillOpacity = styleManager.getFillOpacity();

    if (selectionManager.getSelectedElements().length == 1) {
      var selectedElement = selectionManager.getSelectedElements()[0];
      fillColor = FNISVGEditorElementStylingUtils.getStyle(selectedElement, "fill");
      fillOpacity = FNISVGEditorElementStylingUtils.getStyle(selectedElement, "fill-opacity");
    }

    this._openColorPicker("fill", event.component, fillColor, fillOpacity);
  },
  _onStrokeStyleButtonClick : function(event) {
    var styleManager = this.getEditorInstance().getStyleManager();
    this._openStrokeStyleDialog(event.component);
  },
  _openStrokeStyleDialog : function(button) {
    var styleManager = this.getEditorInstance().getStyleManager();
    var selectionManager = this.getEditorInstance().getSelectionManager();
    var dashArray = styleManager.getStrokeDashArray();
    var strokeWidth = styleManager.getStrokeWidth();

    if (selectionManager.getSelectedElements().length == 1) {
      var selectedElement = selectionManager.getSelectedElements()[0];
      dashArray = FNISVGEditorElementStylingUtils.getStyle(selectedElement, "stroke-dasharray");
      strokeWidth = FNISVGEditorElementStylingUtils.getStyle(selectedElement, "stroke-width");
    }

    var dialog = this.getEditorInstance().openDialog({
      customCSS: this.getPluginURL() + '/dialog.css'      
    });
    
    dialog.domNode.addClassName("FNISVGEditorStylePlugin_StrokeStyleDialog");
    dialog.domNode.setStyle({
      opacity: 0
    });
    
    var _this = this;
    
    dialog.addListener("onLoad", function (event) {
      var dlg = new FNISVGEditorStylePlugin_StrokeStyleDialog(button, _this, strokeWidth, dashArray);
      dialog.getContentElement().appendChild(dlg.domNode);
      dlg.componentOnDOM();
      
      dlg.addListener("okClick", function (event) {
        var selectedElements = _this.getEditorInstance().getSelectionManager().getSelectedElements();
        for ( var i = 0; i < selectedElements.length; i++) {
          var element = selectedElements[i];
          FNISVGEditorElementStylingUtils.setStyles(element, {
            'stroke-dasharray' : event.strokeDashArray,
            'stroke-width' : event.strokeWidth
          });
        }
        
        _this.getEditorInstance().getStyleManager().setStrokeDashArray(event.strokeDashArray);
        _this.getEditorInstance().getStyleManager().setStrokeWidth(event.strokeWidth);
        
        dialog.close();
      });
      
      dlg.addListener("cancelClick", function (event) {
        dialog.close();
      });
      
      dialog.setHeight(135);
      var offset = button.domNode.cumulativeOffset();
      var dims = button.domNode.getDimensions();
      dialog.domNode.setStyle({
        left: (offset.left) + 'px',
        top: (offset.top + dims.height) + 'px'
      });

      dialog.show();
    });    
  },
  _openColorPicker : function (mode, button, color, opacity) {
    var dialog = this.getEditorInstance().openDialog({
      customCSS: this.getPluginURL() + '/dialog.css'      
    });
    
    dialog.domNode.addClassName("FNISVGEditorStylePlugin_ColorPickerDialog");
    dialog.domNode.setStyle({
      opacity: 0
    });
    
    var _this = this;
    
    dialog.addListener("onLoad", function (event) {
      this._mode = mode;
      
      var r,g,b,a;
      
      if (color == 'none') {
        r = g = b = 255;
        a = 0;
      } else {
        r = parseInt(color.substring(1, 3), 16);
        g = parseInt(color.substring(3, 5), 16);
        b = parseInt(color.substring(5, 7), 16);
        a = opacity;
      }
      
      var colorpicker = new FNISVGEditorStylePlugin_ColorPicker({
        gfxPath : _this.getPluginURL() + '/gfx/',
        value: {
          r: r,
          g: g,
          b: b,
          a: a
        }
      });
      
      Event.observe(dialog.getFrameWindow(), "mouseup", colorpicker._MouseUpListener);
      Event.observe(dialog.getFrameDocument(), "mouseup", colorpicker._MouseUpListener);

      var okButton = $(document.createElement("input"));
      okButton.type = 'submit';
      okButton.id = "stylePlugin_colorPickerDialogOkButton";
      okButton.value = SVGLocale.getText('stylePlugin_colorPickerDialogOkButton');
      Event.observe(okButton, "click", function (event) {
        switch (mode) {
          case 'stroke':
            _this._setStrokeColor(colorpicker.getColor(), colorpicker.getOpacity());
          break;
          case 'fill':
            _this._setFillColor(colorpicker.getColor(), colorpicker.getOpacity());
          break;
        }
        
        dialog.close();
      });
      
      var cancelButton = $(document.createElement("input"));
      cancelButton.type = 'submit';
      cancelButton.id = "stylePlugin_colorPickerDialogCancelButton";
      cancelButton.value = SVGLocale.getText('stylePlugin_colorPickerDialogCancelButton');
      Event.observe(cancelButton, "click", function (event) {
        dialog.close();
      });
      
      var buttonsContainer = $(document.createElement("div"));
      buttonsContainer.id = 'stylePlugin_colorPickerDialogButtonsContainer';
      buttonsContainer.appendChild(okButton);
      buttonsContainer.appendChild(cancelButton);
      
      dialog.addContentComponent(colorpicker);
      dialog.getContentElement().appendChild(buttonsContainer);
      dialog.setHeight(204);
      
      var offset = button.domNode.cumulativeOffset();
      var dims = button.domNode.getDimensions();
      dialog.domNode.setStyle({
        left: (offset.left) + 'px',
        top: (offset.top + dims.height) + 'px'
      });

      dialog.show();
    });
  }
});

FNISVGEditorStylePlugin_ColorButton = Class.create(FNIPushButton, {
  initialize : function($super, color, opacity, plugin) {
    $super( {}, "FNISVGEditorStylePlugin_ColorButton");

    this._backgroundElement = new Element("div", {
      className: "FNISVGEditorStylePlugin_ColorButtonBackground",
      style: 'backgroundImage: url(' + plugin.getPluginURL() + '/gfx/samplebg.gif)'
    });
    this._colorElement = new Element("div", {
      className: 'FNISVGEditorStylePlugin_ColorButtonColor'
    });
    this.domNode.appendChild(this._backgroundElement);
    this.domNode.appendChild(this._colorElement);

    this.setColor(color);
    this.setOpacity(opacity);
  },
  setColor : function(color) {
    this._colorElement.style.backgroundColor = color;
    this._color = color;
  },
  setOpacity : function(opacity) {
    this._colorElement.style.opacity = opacity;
    this._opacity = opacity;
  },
  setTooltip: function ($super, text) {
    $super(text);
    
    this._backgroundElement.setAttribute("title", text);
    this._colorElement.setAttribute("title", text);
  }
});

FNISVGEditorStylePlugin_StrokeStyleDialog = Class.create(FNIPanel, {
  initialize : function($super, button, plugin, strokeWidth, strokeDashArray) {
    $super( {});

    this._button = button;

    this._widthUpBtn = new Element("div", {
      className : 'fniSVGEditor_StylePlugin_strokeButtonUp',
      style : "background-image: url(" + plugin.getPluginURL() + '/gfx/uarr.png);'
    });
    
    this._widthDownBtn = new Element("div", {
      className : 'fniSVGEditor_StylePlugin_strokeButtonDown',
      style : "background-image: url(" + plugin.getPluginURL() + '/gfx/darr.png);'
    });

    this._widthUpButtonClickListener = this._onWidthUpButtonClick.bindAsEventListener(this);
    this._widthDownButtonClickListener = this._onWidthDownButtonClick.bindAsEventListener(this);

    Event.observe(this._widthUpBtn, "click", this._widthUpButtonClickListener);
    Event.observe(this._widthDownBtn, "click", this._widthDownButtonClickListener);

    this._okButtonClickListener = this._onOkButtonClick.bindAsEventListener(this);
    this._okButton = new Element("div", {
      className : "fniSVGEditor_StylePlugin_strokeStyleButton"
    }).update(SVGLocale.getText('stylePlugin_strokeStyleDialogOkButton'));
    
    Event.observe(this._okButton, "click", this._okButtonClickListener);

    this._cancelButton = new Element("div", {
      className : "fniSVGEditor_StylePlugin_strokeStyleButton"
    }).update(SVGLocale.getText('stylePlugin_strokeStyleDialogCancelButton'));
    
    this._cancelButtonClickListener = this._onCancelButtonClick.bindAsEventListener(this);
    Event.observe(this._cancelButton, "click", this._cancelButtonClickListener);

    this._selectButtonClickListener = this._onSelectButtonClick.bindAsEventListener(this);
    this._selectButton = new Element("div", {
      className : "fniSVGEditor_StylePlugin_strokeStyleSelectButton",
      style : "background-image: url(" + plugin.getPluginURL() + '/gfx/darr.png);',
      'display' : 'none'
    });
    
    Event.observe(this._selectButton, "click", this._selectButtonClickListener);

    this._solidSelection = new Element("div", {
      className : "fniSVGEditor_StylePlugin_strokeStyleSelection fniSVGEditor_StylePlugin_strokeStyleSelectionSolid",
      style : "background-image: url(" + plugin.getPluginURL() + '/gfx/styles.png);',
      _strokeStyle : 'solid'
    });
    this._dottedSelection = new Element("div", {
      className : "fniSVGEditor_StylePlugin_strokeStyleSelection fniSVGEditor_StylePlugin_strokeStyleSelectionDotted",
      style : "background-image: url(" + plugin.getPluginURL() + '/gfx/styles.png);',
      _strokeStyle : 'dotted'
    });
    this._dashedSelection = new Element("div", {
      className : "fniSVGEditor_StylePlugin_strokeStyleSelection fniSVGEditor_StylePlugin_strokeStyleSelectionDashed",
      style : "background-image: url(" + plugin.getPluginURL() + '/gfx/styles.png);',
      _strokeStyle : 'dashed'
    });

    this._selectionClickListener = this._onSelectionClick.bindAsEventListener(this);

    Event.observe(this._solidSelection, "click", this._selectionClickListener);
    Event.observe(this._dottedSelection, "click", this._selectionClickListener);
    Event.observe(this._dashedSelection, "click", this._selectionClickListener);

    this._styleDropDown = new Element("div", {
      id : "fniSVGEditor_StylePlugin_strokeStyleSelectDropdown",
      "style" : "display: none"
    });
    this._styleDropDown.appendChild(this._solidSelection);
    this._styleDropDown.appendChild(this._dottedSelection);
    this._styleDropDown.appendChild(this._dashedSelection);

    this._selectDisplay = new Element("div", {
      className : "fniSVGEditor_StylePlugin_strokeStyleSelectDisplay",
      style : "background-image: url(" + plugin.getPluginURL() + '/gfx/styles.png);'
    });
    this._widthDisplay = new Element("div", {
      className : 'fniSVGEditor_StylePlugin_strokeWidthDisplay'
    }).update('0');
    
    var strokeWidthButtonsElement = new Element("div", { className : 'fniSVGEditor_StylePlugin_strokeWidthBtns'});
    strokeWidthButtonsElement.appendChild(this._widthUpBtn);
    strokeWidthButtonsElement.appendChild(this._widthDownBtn);
    
    var strokeWidthElement = new Element("div", { className : 'fniSVGEditor_StylePlugin_strokeWidth' });
    strokeWidthElement.appendChild(this._widthDisplay);
    strokeWidthElement.appendChild(strokeWidthButtonsElement);
    
    var strokeStyleSelectElement = new Element("div", { className : "fniSVGEditor_StylePlugin_strokeStyleSelect" });
    strokeStyleSelectElement.appendChild(this._selectDisplay);
    strokeStyleSelectElement.appendChild(this._selectButton);
    
    var strokeStyleElement = new Element("div", { className : "fniSVGEditor_StylePlugin_strokeStyle" });
    strokeStyleElement.appendChild(strokeStyleSelectElement);
    strokeStyleElement.appendChild(this._styleDropDown);
    
    var strokeStyleButtons = new Element("div", { className : "fniSVGEditor_StylePlugin_strokeStyleButtons" });
    strokeStyleButtons.appendChild(this._okButton);
    strokeStyleButtons.appendChild(this._cancelButton);
    
    this.domNode = new Element("div", { className : "fniSVGEditor_StylePlugin_strokeStyleDialog" });
    this.domNode.appendChild(strokeWidthElement);
    this.domNode.appendChild(strokeStyleElement);
    this.domNode.appendChild(strokeStyleButtons);

    /** TODO: move to conf */
    if (strokeDashArray == 1)
      this._setStrokeStyle('dotted');
    else if (strokeDashArray == 5)
      this._setStrokeStyle('dashed');
    else
      this._setStrokeStyle('solid');

    this._setStrokeWidth(strokeWidth);
  },
  close : function() {
    Event.stopObserving(this._okButton, "click", this._okButtonClickListener);
    Event.stopObserving(this._cancelButton, "click", this._cancelButtonClickListener);
    Event.stopObserving(this._selectButton, "click", this._selectButtonClickListener);
    Event.stopObserving(this._solidSelection, "click", this._selectionClickListener);
    Event.stopObserving(this._dottedSelection, "click", this._selectionClickListener);
    Event.stopObserving(this._dashedSelection, "click", this._selectionClickListener);
    Event.stopObserving(this._widthUpBtn, "click", this._widthUpButtonClickListener);
    Event.stopObserving(this._widthDownBtn, "click", this._widthDownButtonClickListener);
  },
  _onSelectButtonClick : function(event) {
    if (this._styleDropDown.visible() != true) {
      this._styleDropDown.show();
    } else {
      this._styleDropDown.hide();
    }
  },
  _onOkButtonClick : function(event) {
    this.fire("okClick", {
      strokeDashArray : this._dashArray,
      strokeWidth : this._strokeWidth
    });
  },
  _onCancelButtonClick : function(event) {
    this.fire("cancelClick", {});
  },
  _onSelectionClick : function(event) {
    var element = Event.element(event);
    this._setStrokeStyle(element.getAttribute('_strokeStyle'));
    this._styleDropDown.hide();
  },
  _onWidthUpButtonClick : function(event) {
    this._strokeWidth++;
    this._setStrokeWidth(this._strokeWidth);
  },
  _onWidthDownButtonClick : function(event) {
    if (this._strokeWidth > 0) {
      this._strokeWidth--;
      this._setStrokeWidth(this._strokeWidth);
    }
  },
  _setStrokeStyle : function(strokeStyle) {
    switch (strokeStyle) {
    case 'dotted':
      this._selectDisplay.setStyle( {
        'backgroundPosition' : '0px -20px'
      });
      this._dashArray = '1';
      break;
    case 'dashed':
      this._selectDisplay.setStyle( {
        'backgroundPosition' : '0px -40px'
      });
      this._dashArray = '5';
      break;
    default:
      this._selectDisplay.setStyle( {
        'backgroundPosition' : '0px 0px'
      });
      this._dashArray = '0';
      break;
    }
  },
  _setStrokeWidth : function(width) {
    this._widthDisplay.innerHTML = width;
    this._strokeWidth = parseInt(width);
  }
});

FNISVGEditorStylePlugin_ColorPicker = Class.create(GUIComponent, {
  initialize : function($super, options) {
    $super(options, "FNIColorPicker");
    this._initialized = false;
    this.domNode = new Element("div", {className: "fniSVGEditor_StylePlugin_fniColorPicker"});

    this._pointerWidth = 17;
    this._pointerHeight = 17;

    /* map */

    this._mapContainer = new Element("div", {className: "fniSVGEditor_StylePlugin_fniColorPickerMapContainer"}); 
    this._mapColorElement = new Element("div", {className: "fniSVGEditor_StylePlugin_fniColorPickerMapColor"}); 
    this._mapHueElement = new Element("div", {className: "fniSVGEditor_StylePlugin_fniColorPickerMapHue", style: 'background-image:url(' + options.gfxPath + 'huemap.png)'}); 
    this._mapPointer = new Element("div", {className: "fniSVGEditor_StylePlugin_fniColorPickerMapPointer"}); 
    
    this._mapPointerN = new Element("div");
    this._mapPointer.appendChild(this._mapPointerN);
    this._mapPointerE = new Element("div");
    this._mapPointer.appendChild(this._mapPointerE);
    this._mapPointerS = new Element("div");
    this._mapPointer.appendChild(this._mapPointerS);
    this._mapPointerW = new Element("div");
    this._mapPointer.appendChild(this._mapPointerW);

    this._mapContainer.appendChild(this._mapPointer);
    this._mapContainer.appendChild(this._mapColorElement);
    this._mapContainer.appendChild(this._mapHueElement);
    
    this.disableSelection(this._mapContainer);
    this.disableSelection(this._mapColorElement);
    this.disableSelection(this._mapHueElement);

    /* hue bar */

    this._hueBarContainer =  new Element("div", {className: "fniSVGEditor_StylePlugin_fniColorPickerHueBarContainer"});
    this._hueBar = new Element("div", {className: "fniSVGEditor_StylePlugin_fniColorPickerHueBar", style: 'background-image:url(' + options.gfxPath + 'huebar.png)'});
    this._hueBarSelect = new Element("div", {className: "fniSVGEditor_StylePlugin_fniColorPickerHueBarSelect", style: 'background-image:url(' + options.gfxPath + 'vertknob.png)'});
        
    this._hueBarContainer.appendChild(this._hueBar);
    this._hueBarContainer.appendChild(this._hueBarSelect);    
    
    /* opacity bar */

    this._opacityBarContainer = new Element("div", {className: "fniSVGEditor_StylePlugin_fniColorPickerOpacityBarContainer", style: 'background-image:url(' + options.gfxPath + 'opacitybar.png)'}); 
    this._opacityBar = new Element("div", {className: "fniSVGEditor_StylePlugin_fniColorPickerOpacityBar", style:'background-image:url(' + options.gfxPath + 'opacitybar.png)'});
    this._opacityBarSelect = new Element("div", {className: "fniSVGEditor_StylePlugin_fniColorPickerOpacityBarSelect", style: 'background-image:url(' + options.gfxPath + 'horzknob.png)'});
    this.disableSelection(this._opacityBarSelect);
    this._opacityBarBackground = new Element("div", {className: "fniSVGEditor_StylePlugin_fniColorPickerOpacityBarBackground", style: 'background-image:url(' + options.gfxPath + 'samplebg.gif)'});

    this._opacityBarContainer.appendChild(this._opacityBarBackground);
    this._opacityBarContainer.appendChild(this._opacityBar);
    this._opacityBarContainer.appendChild(this._opacityBarSelect);
    
    /* color display */

    this._colorDisplayContainer = new Element("div", {className: "fniSVGEditor_StylePlugin_fniColorPickerColorDisplayContainer"}); 
    this._colorDisplay = new Element("div", {className: "fniSVGEditor_StylePlugin_fniColorPickerColorDisplay"});
    this._colorDisplayBackground = new Element("div", {className: "fniSVGEditor_StylePlugin_fniColorPickerColorDisplayBackground", style: 'background-image:url(' + options.gfxPath + 'samplebg.gif)'});
    this._colorDisplayContainer.appendChild(this._colorDisplayBackground);
    this._colorDisplayContainer.appendChild(this._colorDisplay);

    this.domNode.appendChild(this._opacityBarContainer);
    this.domNode.appendChild(this._mapContainer);
    this.domNode.appendChild(this._hueBarContainer);
    this.domNode.appendChild(this._colorDisplayContainer);
    this.disableSelection();

    /* Listeners */
    
    this.addListener("DOMStatusChange", this, this._onDOMStatusChange);
    this._MouseUpListener = this._onMouseUp.bindAsEventListener(this);
    this._domNodeMouseMoveListener = this._onDomNodeMouseMove.bindAsEventListener(this);
    
    this._mapMouseDownListener = this._onMapMouseDown.bindAsEventListener(this);
    this._hueBarSelectMouseDownListener = this._onHueBarSelectMouseDown.bindAsEventListener(this);
    this._opacityBarSelectMouseDownListener = this._onOpacityBarSelectMouseDown.bindAsEventListener(this);
    
    Event.observe(document, "mouseup", this._MouseUpListener);
    Event.observe(this.domNode, "mousemove", this._domNodeMouseMoveListener);
    
    Event.observe(this._mapHueElement, "mousedown", this._mapMouseDownListener);
    Event.observe(this._hueBarSelect, "mousedown", this._hueBarSelectMouseDownListener);
    Event.observe(this._opacityBarSelect, "mousedown", this._opacityBarSelectMouseDownListener);
    
    this._mapMouseDown = false;
    this._hueMouseDown = false;
    this._opacityMouseDown = false;
    
    this._color = new FNISVGEditorColorRGB(options.value.r / 255, options.value.g / 255, options.value.b / 255, options.value.a);
    this._mapColor = new FNISVGEditorColorRGB(options.value.r / 255, options.value.g / 255, options.value.b / 255, options.value.a);
    
    if (this._mapColor.toHSV().getSaturation() < 0.01) {
      this._mapColor.setRGB(1, 0, 0);
    }
  },
  _onDOMStatusChange : function(event) {
    if (event.action == 'added') {
      this._mapContainerCursor = this._mapContainer.getStyle("cursor");
      this._updateHueBarPosition();
      this._updateMapCursor();
      this._updateMapColor();
      this._updatePreview();
      this._updateOpacitySelect();
    }
  },
  getOpacity : function() {
    return this._color.getAlpha();
  },
  getColor : function() {
    return this._color.toWebColor();   
  },
  _updatePreview: function () {
    this._colorDisplay.setStyle({
      backgroundColor : this._color.toWebColor(),
      opacity: this._color.getAlpha()
    });
  },
  _updateOpacitySelect: function () {
    var opacityBarWidth = this._opacityBar.getLayout().get("width");
    var knobPosition = Math.round(this._color.getAlpha() * opacityBarWidth);
    
    this._opacityBarSelect.setStyle({
      backgroundPosition: knobPosition + 'px 0px'
    });
  },
  _updateMapColor: function () {
    this._mapColorElement.setStyle( {
      backgroundColor : this._mapColor.toWebColor()
    });
  },
  _updateHueBarPosition: function () {
    var hsv = this._mapColor.toHSV();
    var hueBarHeight = this._hueBar.getLayout().get("height");
    var knobPosition = Math.round((1 - hsv.getHue()) * hueBarHeight);
    this._hueBarSelect.setStyle({
      backgroundPosition: '0px ' + knobPosition + 'px'
    });
  },
  _updateMapCursor: function (value, saturation) {
    var hsv = this._color.toHSV();
    var dims = this._mapColorElement.getDimensions();
    
    var x = (hsv.getValue()||value) * dims.width;
    var y = (1 - (saturation||hsv.getSaturation())) * dims.height;
    
    this._mapPointer.setStyle( {
      marginLeft: (x - (this._pointerWidth / 2)) + 'px',
      marginTop: (y - (this._pointerHeight / 2)) + 'px'
    });

    var pWH = Math.round(this._pointerWidth / 2);
    var pHH = Math.round(this._pointerHeight / 2);
    var cW = 1;
    var inv = this._color.clone();
    inv.invert();
    var oC = inv.toWebColor();
    
    this._mapPointerN.setStyle( {
      'position' : 'absolute',
      'width' : cW + 'px',
      'height' : (pWH - 1) + 'px',
      'left' : (pWH - cW) + 'px',
      'backgroundColor' : oC
    });

    this._mapPointerE.setStyle( {
      'position' : 'absolute',
      'width' : (pWH - 1) + 'px',
      'height' : cW + 'px',
      'left' : (pWH - cW + 1) + 'px',
      'top' : (pWH - 1) + 'px',
      'backgroundColor' : oC
    });

    this._mapPointerS.setStyle( {
      'position' : 'absolute',
      'width' : cW + 'px',
      'height' : (pWH - 1) + 'px',
      'left' : (pWH - cW) + 'px',
      'top' : (pWH) + 'px',
      'backgroundColor' : oC
    });

    this._mapPointerW.setStyle( {
      'position' : 'absolute',
      'width' : (pWH - 1) + 'px',
      'height' : cW + 'px',
      'left' : '0px',
      'top' : (pWH - 1) + 'px',
      'backgroundColor' : oC
    });
  },
  _onMouseUp : function() {
    this._mapMouseDown = false;
    this._hueMouseDown = false;
    this._opacityMouseDown = false;
    
    this._mapContainer.setStyle( {
      cursor : this._mapContainerCursor
    });
  },
  _onDomNodeMouseMove:function(event) {
    switch (true) {
      case this._mapMouseDown:
        Event.stop(event);
        
        var mouseX = Event.pointerX(event);
        var mouseY = Event.pointerY(event);
        
        var dims = this._mapColorElement.getDimensions();
        var offs = this._mapColorElement.cumulativeOffset();
        
        if (mouseX < offs.left)
          mouseX = offs.left;
        else if (mouseX > (offs.left + dims.width))
          mouseX = offs.left + dims.width;
        
        if (mouseY < offs.top)
          mouseY = offs.top;
        else if (mouseY > (offs.top + dims.height))
          mouseY = offs.top + dims.height;
        
        this._pickColorByMouse(mouseX, mouseY);
      break;
      case this._hueMouseDown:
        Event.stop(event);
        
        var hueBarOffset = this._hueBar.cumulativeOffset();
        var hueBarHeight = this._hueBar.getLayout().get("height");
        var mouseY = Event.pointerY(event);
        
        if ((mouseY >= hueBarOffset.top) && (mouseY <= (hueBarOffset.top + hueBarHeight))) {
          var hueValue = ((mouseY - hueBarOffset.top) / hueBarHeight) * 360;
          this._setColorByHueBar(360 - hueValue);
        }
        
        this._updateHueBarPosition();
      break;
      case this._opacityMouseDown:
        Event.stop(event);
        
        var opacityBarOffset = this._opacityBar.cumulativeOffset();
        var opacityBarWidth = this._opacityBar.getLayout().get("width");
        var mouseX = Event.pointerX(event);
        
        if ((mouseX >= opacityBarOffset.left) && (mouseX <= (opacityBarOffset.left + opacityBarWidth))) {
          var opacityValue = ((mouseX - opacityBarOffset.left) / opacityBarWidth);
          this._setOpacity(opacityValue);
        }
        
        this._updateOpacitySelect();
      break;
    }
  },
  _onMapMouseDown: function (event) {
    this._mapMouseDown = true;
    this._pickColorByMouse(Event.pointerX(event), Event.pointerY(event));
  },
  _onHueBarSelectMouseDown:function(event) {
    this._hueMouseDown = true;
    var hueBarOffset = this._hueBar.cumulativeOffset();
    var hueBarHeight = this._hueBar.getLayout().get("height");
    var mouseY = Event.pointerY(event);
    
    if ((mouseY >= hueBarOffset.top) && (mouseY <= (hueBarOffset.top + hueBarHeight))) {
      var hueValue = ((mouseY - hueBarOffset.top) / hueBarHeight) * 360;
      this._setColorByHueBar(360 - hueValue);
    }
    
    this._updateHueBarPosition();
  },
  _onOpacityBarSelectMouseDown:function(event) {
    this._opacityMouseDown = true;
    var opacityBarOffset = this._opacityBar.cumulativeOffset();
    var opacityBarWidth = this._opacityBar.getLayout().get("width");
    var mouseX = Event.pointerX(event);
    
    if ((mouseX >= opacityBarOffset.left) && (mouseX <= (opacityBarOffset.left + opacityBarWidth))) {
      var opacityValue = ((mouseX - opacityBarOffset.left) / opacityBarWidth);
      this._setOpacity(opacityValue);
    }
    
    this._updateOpacitySelect();
  },
  
  _pickColorByMouse: function (mouseX, mouseY) {
    var dims = this._mapColorElement.getDimensions();
    var offs = this._mapColorElement.cumulativeOffset();
    
    var value = (mouseX - offs.left) / dims.width;
    var saturation = (mouseY - offs.top) / dims.height;
    
    var mapColorHSV = this._mapColor.toHSV();
    
    mapColorHSV.setAlpha(this._color.getAlpha());
    mapColorHSV.setValue(value);
    mapColorHSV.setSaturation(1 - saturation);
    
    this._color = mapColorHSV.toRGB();
    
    this._updateMapCursor(value, 1 - saturation);
    this._updatePreview();
  },
  _setOpacity: function (value) {
    this._color.setAlpha(value);
    this._updateOpacitySelect();
    this._updatePreview();
  },
  _setColorByHueBar: function (value) {
    var hue = value / 360;
    if (hue > 0.9999)
      hue = 0.9999;
    
    this._mapColor = new FNISVGEditorColorHSV(hue, 1, 1, this._color.getAlpha()).toRGB();
    var currentHSV = this._color.toHSV();
    
    this._color = new FNISVGEditorColorHSV(hue, currentHSV.getSaturation(), currentHSV.getValue(), this._color.getAlpha()).toRGB();
    
    this._updateMapColor();
    this._updatePreview();
    this._updateMapCursor();
  }
});

document.fire("svgEditorPluginLoaded:style", {
  pluginClass : FNISVGEditorStylePlugin
});
